using System.Reflection;
using OpenAI.Builders;
using OpenAI.ObjectModels.RequestModels;
using static OpenAI.ObjectModels.SharedModels.PropertyDefinition;

namespace MultiAgentsShared;

public class ToolSetCollection
{
    public class ToolSet
    {
        public string ToolSetName;
        public string ToolSetDescription;
        //<ToolDef, RequireApproval>
        public Dictionary<ToolDefinition, bool> ToolDefinitions;

        public ToolSet(string toolSetName, string toolSetDescription)
        {
            ToolSetName = toolSetName;
            ToolSetDescription = toolSetDescription;
            ToolDefinitions = new();
        }
    }

    public bool IfToolCallsRequireApproval(List<ToolCall> toolCalls)
    {
        //if any function needs approval then its true
        var anyToolCallRequireApproval = false;
        ToolDefinition toolDefFound = null;
        foreach (var toolCall in toolCalls)
        {
            //find toolSet
            foreach (ToolSet toolSet in ToolSets)
            {
                var toolDef = toolSet.ToolDefinitions.FirstOrDefault(td => td.Key.Function.Name == toolCall.FunctionCall.Name);
                if (toolDef.Key == null)
                {
                    continue;
                }
                else
                {
                    //require approval
                    toolDefFound = toolDef.Key;
                    if (toolDef.Value == true)
                    {
                        anyToolCallRequireApproval = true;
                        break;
                    }
                }
            }
        }


        if (toolDefFound == null)
        {
            throw new Exception("ToolDef Not Found");
        }
        else
        {
            return anyToolCallRequireApproval;
        }
    }

    public static List<ToolSet> ToolSets { get; protected set; }

    public ToolSetCollection()
    {
        GenerateToolSet();
    }

    public string GetToolSetsJson()
    {
        List<ToolDefinition> tools = new List<ToolDefinition>();
        foreach (var t in ToolSets)
        {
            foreach (var toolDef in t.ToolDefinitions)
            {
                tools.Add(toolDef.Key);
            }
        }

        var jsonData = ToolsRelatedJsonUtils.SerializeToolDefs(tools);
        return jsonData;
    }


    private void GenerateToolSet()
    {
        ToolSets = new List<ToolSet>();

        var assembly = Assembly.GetExecutingAssembly(); // Gets the currently executing assembly
        var types = assembly.GetTypes(); // Retrieves all types defined in the assembly

        foreach (var type in types)
        {
            // Filter types to avoid processing system types or types from external libraries
            // Additionally, check if the class has the GptToolSet attribute
            if (type.IsClass == false || type.IsAbstract)
            {
                continue;
            }

            var toolSetAttr = type.GetCustomAttribute<GptToolSetAttribute>();
            if (toolSetAttr == null)
            {
                continue;
            }

            var newToolSet = new ToolSet(toolSetAttr.Name, toolSetAttr.Description);
            ToolSets.Add(newToolSet);


            var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);

            foreach (var method in methods)
            {
                var functionAttr = method.GetCustomAttribute<GptFuncAttribute>();
                if (functionAttr == null)
                {
                    continue;
                }

                var builder = new FunctionDefinitionBuilder(method.Name, functionAttr.Description);

                foreach (var parameter in method.GetParameters())
                {
                    var paramAttr = parameter.GetCustomAttribute<GptFuncParamAttribute>();
                    if (paramAttr != null)
                    {
                        var paramType = parameter.ParameterType.ToString();

                        if (parameter.ParameterType.BaseType == typeof(Enum))
                        {
                            var enumValues = Enum.GetNames(parameter.ParameterType);
                            var enumValueList = new List<string>(enumValues);
                            builder.AddParameter(parameter.Name, DefineEnum(enumValueList, paramAttr.Description));
                        }
                        else
                        {
                            switch (paramType)
                            {
                                case "System.String":
                                    builder.AddParameter(parameter.Name, DefineString(paramAttr.Description));
                                    break;
                                case "System.Int32":
                                    builder.AddParameter(parameter.Name, DefineInteger(paramAttr.Description));
                                    break;
                                case "System.Boolean":
                                    builder.AddParameter(parameter.Name, DefineBoolean(paramAttr.Description));
                                    break;
                                default:
                                    //DefineNumber(string? description = null)
                                    //DefineEnum(List<string> enumList, string? description = null)
                                    //DefineObject(IDictionary<string, PropertyDefinition>? properties, IList<string>? required, bool? additionalProperties, string? description, IList<string>? @enum)
                                    //DefineArray(PropertyDefinition? arrayItems = null)
                                    //DefineNull(string? description = null)
                                    Debug.LogError($"Failed To Get AttributeType: {paramType}");
                                    break;
                            }
                        }
                    }
                }

                var toolDefinition = ToolDefinition.DefineFunction(builder.Validate().Build());
                newToolSet.ToolDefinitions.Add(toolDefinition, functionAttr.RequireApproval);
            }
        }
    }
}
